﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Globalization;
using System.Net;
using System.IO;
using System.Web.Routing;
using System.Web.Mvc;
using PaymentHelper.Core.Extension;
using PaymentHelper.Methods.PaypalStandard.Models;
using PaymentHelper.Core.RelatedObject;
using PaymentHelper.Core.CommonUtils;

namespace PaymentHelper.Methods.PaypalStandard
{
    public class PaypalStandardProccesor : BaseExtension,IPaymentMethod
    {
        public bool SupportCapture
        {
            get { return false; }
        }

        public bool SupportRefund
        {
            get { return false; }
        }

        public bool SupportVoid
        {
            get { return false; }
        }

        public PaymentMethodType PaymentMethodType
        {
            get
            {
                return PaymentHelper.PaymentMethodType.Redirection;
            }            
        }

        public decimal GetHandlingFee()
        {
            return GetSetting("HandlingFee");
        }

        public void ProcessPayment(ProcessPaymentRequest processPaymentRequest)
        {
            StringBuilder urlBuilder = new StringBuilder();
            urlBuilder.Append(GetPaypalUrl());

            if (GetSetting("PassDetailListItems"))
            {
                urlBuilder.AppendFormat("?cmd={0}&business={1}&upload=1", "_cart", GetSetting("BusinessEmail"));

                int iCount = 1;
                decimal cartTotal = decimal.Zero;

                //Products
                var listItems = processPaymentRequest.Order.ListOrderItem;

                foreach (var item in listItems)
                {
                    decimal roundedPrice = Math.Round(item.Price, 2);
                    urlBuilder.AppendFormat("&item_name_{0}={1}", iCount, item.Name);
                    urlBuilder.AppendFormat("&amount_{0}={1}", iCount, roundedPrice.ToString("0.00", CultureInfo.InvariantCulture));
                    urlBuilder.AppendFormat("&quantity_{0}={1}", iCount, item.Quantiy);
                    iCount++;
                    cartTotal += item.TotalPrice;
                }

                //Shipping
                var roundedShippingFee = Math.Round(processPaymentRequest.Order.ShippingFee, 2);
                if (roundedShippingFee > decimal.Zero)
                {
                    urlBuilder.AppendFormat("&item_name_{0}={1}", iCount, "Shipping Fee");
                    urlBuilder.AppendFormat("&amount_{0}={1}", iCount, roundedShippingFee.ToString("0.00", CultureInfo.InvariantCulture));
                    urlBuilder.AppendFormat("&quatity_{0}={1}", iCount, 1);
                    iCount++;
                    cartTotal += processPaymentRequest.Order.ShippingFee;
                }

                //HandlingFee
                var roundedHandlingFee = Math.Round(GetHandlingFee(), 2);
                if (roundedHandlingFee > decimal.Zero)
                {
                    urlBuilder.AppendFormat("&item_name_{0}={1}", iCount, "Handling Fee");
                    urlBuilder.AppendFormat("&amount_{0}={1}", iCount, roundedHandlingFee.ToString("0.00", CultureInfo.InvariantCulture));
                    urlBuilder.AppendFormat("&quatity_{0}={1}", iCount, 1);
                    iCount++;
                    cartTotal += GetHandlingFee();
                }

                //TaxFee
                var roundedTaxFee = Math.Round(processPaymentRequest.Order.TaxFee, 2);
                if (roundedTaxFee > decimal.Zero)
                {
                    urlBuilder.AppendFormat("&item_name_{0}={1}", iCount, "Tax Fee");
                    urlBuilder.AppendFormat("&amount_{0}={1}", iCount, roundedTaxFee.ToString("0.00", CultureInfo.InvariantCulture));
                    urlBuilder.AppendFormat("&quatity_{0}={1}", iCount, 1);
                    iCount++;
                    cartTotal += processPaymentRequest.Order.TaxFee;
                }

                //Other fee
                var roundedOtherFee = Math.Round(processPaymentRequest.Order.OtherFee, 2);
                if (roundedOtherFee > decimal.Zero)
                {
                    urlBuilder.AppendFormat("&item_name_{0}={1}", iCount, "Other Fees");
                    urlBuilder.AppendFormat("&amount_{0}={1}", iCount, roundedOtherFee.ToString("0.00", CultureInfo.InvariantCulture));
                    urlBuilder.AppendFormat("&quatity_{0}={1}", iCount, 1);
                    iCount++;
                    cartTotal += processPaymentRequest.Order.OtherFee;
                }

                if (cartTotal > processPaymentRequest.Order.OrderTotal)
                {
                    //Discount
                    var disCount = cartTotal - processPaymentRequest.Order.OrderTotal;
                    var roundedDisCount = Math.Round(disCount, 2);
                    urlBuilder.AppendFormat("&discount_amount_cart={0}", roundedDisCount.ToString("0.00", CultureInfo.InvariantCulture));
                }
            }
            else
            {
                //only pass order total 
                urlBuilder.AppendFormat("?cmd={0}&business={1}", "_xclick", GetSetting("BusinessEmail"));
                var roundedTotal = Math.Round(processPaymentRequest.Order.OrderTotal, 2);
                urlBuilder.AppendFormat("&item_name={1}&amount={2}",
                    HttpUtility.UrlEncode("Order Id: " + processPaymentRequest.Order.OrderId),
                    roundedTotal.ToString("0.00", CultureInfo.InvariantCulture));
            }

            if (GetSetting("EnableUTF8Encoding"))
                urlBuilder.AppendFormat("&charset={0}", "utf-8");

            urlBuilder.AppendFormat("&no_note=1&currency_code={0}", HttpUtility.UrlEncode(processPaymentRequest.Order.CustomerCurrency));
            
            string returnUrl = PHUtility.GetSiteUrl() + GetSetting("ReturnUrl");
            string cancleUrl = PHUtility.GetSiteUrl() + GetSetting("CancleUrl");
            urlBuilder.AppendFormat("&rm={0}", 2);
            urlBuilder.AppendFormat("&return={0}&cancel_return={1}", HttpUtility.UrlEncode(returnUrl), HttpUtility.UrlEncode(cancleUrl));

            //Instant Payment Notification (IPN)
            if (GetSetting("EnableIPN"))
            {
                string ipnUrl = GetSetting("IpnUrl");
                if (String.IsNullOrWhiteSpace(ipnUrl))
                    ipnUrl = returnUrl;

                urlBuilder.AppendFormat("&notify_url={0}", ipnUrl);
            }

            //Info
            //address
            if (GetSetting("OverrideAddress"))
                urlBuilder.AppendFormat("&address_override={0}", 1);

            urlBuilder.AppendFormat("&first_name={0}", processPaymentRequest.Order.BillingAddress.FirstName);
            urlBuilder.AppendFormat("&last_name={0}", processPaymentRequest.Order.BillingAddress.LastName);
            urlBuilder.AppendFormat("&address1={0}", processPaymentRequest.Order.BillingAddress.Address1);

            if (!string.IsNullOrEmpty(processPaymentRequest.Order.BillingAddress.Address2))
                urlBuilder.AppendFormat("&address2={0}", processPaymentRequest.Order.BillingAddress.Address2);

            if (!string.IsNullOrEmpty(processPaymentRequest.Order.BillingAddress.City))
                urlBuilder.AppendFormat("&city={0}", processPaymentRequest.Order.BillingAddress.City);

            if (processPaymentRequest.Order.BillingAddress.StateProvince != null)
                urlBuilder.AppendFormat("&state={0}", processPaymentRequest.Order.BillingAddress.StateProvince);

            if (processPaymentRequest.Order.BillingAddress.Country != null)
                urlBuilder.AppendFormat("&country={0}", processPaymentRequest.Order.BillingAddress.Country);

            if (!string.IsNullOrEmpty(processPaymentRequest.Order.BillingAddress.ZipPostalCode))
                urlBuilder.AppendFormat("&zip={0}", processPaymentRequest.Order.BillingAddress.ZipPostalCode);

            if (!string.IsNullOrEmpty(processPaymentRequest.Order.BillingAddress.Email))
                urlBuilder.AppendFormat("&email={0}", processPaymentRequest.Order.BillingAddress.Email);

            HttpContext.Current.Response.Redirect(urlBuilder.ToString(),true);
        }

        public CapturePaymentResult Capture(CapturePaymentRequest capturePaymentRequest)
        {
            CapturePaymentResult result = new CapturePaymentResult();
            result.AddError("Captured method not supported !");
            result.PaymentStatus = Core.RelatedObject.PaymentStatus.Canceled;
            result.Success = false;

            return result;
        }

        public RefundPaymentResult Refund(RefundPaymentRequest refundPaymentRequest)
        {
            RefundPaymentResult result = new RefundPaymentResult();
            result.AddError("Refund method not supported !");
            result.PaymentStatus = Core.RelatedObject.PaymentStatus.Canceled;
            result.Success = false;

            return result;
        }

        public VoidPaymentResult Void(VoidPaymentRequest voidPaymentRequest)
        {
            VoidPaymentResult result = new VoidPaymentResult();
            result.AddError("Void method not supported !");
            result.PaymentStatus = Core.RelatedObject.PaymentStatus.Canceled;
            result.Success = false;

            return result;
        }

        public void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues)
        {
            actionName = "Configure";
            controllerName = "PaypalStandard";
            routeValues = new RouteValueDictionary();
            routeValues.Add("Namespaces", "PaymentHelper.Methods.PaypalStandard.Controllers");
            routeValues.Add("area", null);
        }

        public void GetPaymentInfoRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues)
        {
            actionName = "PaymentInfo";
            controllerName = "PaypalStandard";
            routeValues = new RouteValueDictionary();
            routeValues.Add("Namespaces", "PaymentHelper.Methods.PaypalStandard.Controllers");
            routeValues.Add("area", null);
        }

        public void RegisterRoutes(RouteCollection routes)
        {
            routes.MapRoute(
                "PaypalStandard.PaymentInfo",
                "Methods/PaypalStandard/PaymentInfo",
                new { controller = "PaypalStandard", action = "PaymentInfo" },
                new[] { "PaymentHelper.Methods.PaypalStandard.Controllers" }
                );

            routes.MapRoute(
                "PaypalStandard.Configure",
                "Methods/PaypalStandard/PaymentInfo",
                new { controller = "PaypalStandard", action = "PaymentInfo" },
                new[] { "PaymentHelper.Methods.PaypalStandard.Controllers" }
                );

            routes.MapRoute(
                "PaypalStandard.Complete",
                "Methods/PaypalStandard/Complete",
                new { controller = "PaypalStandard", action = "Complete" },
                new[] { "PaymentHelper.Methods.PaypalStandard.Controllers" }
                );
        }

        /// <summary>
        /// Gets Paypal URL
        /// </summary>
        /// <returns></returns>
        private string GetPaypalUrl()
        {
            bool isUseSandbox = GetSetting("UseSandbox");
            return isUseSandbox ? GetSetting("SandboxLink") : GetSetting("PaypalStandardLink");
        }

        public bool VerifyIPN(string frmString, out Dictionary<string, string> values)
        {
            bool bSuccess = false;

            var req = (HttpWebRequest)WebRequest.Create(GetPaypalUrl());
            req.Method = "POST";
            req.ContentType = "application/x-www-form-urlencoded";

            string frmContent = string.Format("{0}&cmd=_notify-validate", frmString);
            req.ContentLength = frmContent.Length;
            using (var sw = new StreamWriter(req.GetRequestStream(), Encoding.Unicode))
            {
                sw.Write(frmContent);
            }

            string response = string.Empty;
            using (var sr = new StreamReader(req.GetResponse().GetResponseStream()))
            {
                response = HttpUtility.UrlDecode(sr.ReadToEnd());
            }

            bSuccess = response.Trim().Equals("VERIFIED", StringComparison.OrdinalIgnoreCase);

            values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            foreach (string str in frmString.Split('&'))
            {
                string line = str.Trim();
                int pos = line.IndexOf('=');
                if (pos > 0)
                {
                    string key = line.Substring(0, pos);
                    string val = line.Substring(pos + 1);
                    values.Add(key, val);
                }
            }

            return bSuccess;
        }

        public void GetPaymentInfo(Core.Order orderCurrent, FormCollection collection)
        {
        }
        
        public PaymentHelper.Core.MVCUtils.PaymentHelperController GetControllerType()
        {
            return new PaypalStandard.Controlers.PaypalStandardController();
        }

        public bool AcceptCurrency(string currencyCode)
        {
            if (string.IsNullOrEmpty(currencyCode))
                return false;

            string currencySetting = GetSetting("CurrencyAccepted");

            if (string.IsNullOrEmpty(currencySetting))
                return false;

            var listCurrency = currencySetting.SplitToListString(',');
            return listCurrency.Contains(currencyCode);
        }
    }
}
